#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <iostream>
#include <arpa/inet.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>

using namespace std;
#define MSG_SIZE 80	// Maksymalna wielkość wiadomości
#define MAX_CLIENTS 95 // Maksymalna liczba podłączonych klientów
#define MYPORT 7400 // Standardowy port serwera
void exitClient(int fd, fd_set *readfds, char fd_array[], int *num_clients) {
	int i;

	close(fd);
	FD_CLR(fd, readfds);
	for (i = 0; i < (*num_clients) - 1; i++)
		if (fd_array[i] == fd)
			break;
	for (; i < (*num_clients) - 1; i++)
		(fd_array[i]) = (fd_array[i + 1]);
	(*num_clients)--;
}

void tokenizestring(const std::string& src, const std::string& delim,
		vector<string>&tokens) {
	string::size_type start = 0;
	string::size_type end;

	tokens.clear();
	for (;;) {
		end = src.find(delim, start);
		if (end == string::npos)
			break;
		tokens.push_back(src.substr(start, end - start));
		start = end + delim.size();
	}
}

int main(int argc, char *argv[]) {
	int i = 0;
	int port;
	int num_clients = 0;
	int server_sockfd, client_sockfd;
	struct sockaddr_in server_address, client_address;
	socklen_t clilen;
	int addresslen = sizeof(struct sockaddr_in);
	int fd;
	char fd_array[MAX_CLIENTS];
	fd_set readfds, testfds, clientfds;
	char msg[MSG_SIZE + 1];
	char kb_msg[MSG_SIZE + 10];
	int result;
	string name;
	clilen = sizeof(client_address);
	bool black = 0;
	/*Uruchomienie serwera*/
	if (argc == 1 || argc == 3) {
		/*Sprawdzanie parametrów programu. Pierwszy parametr numer portu, drugi parametr nazwa hosta. Domyślny port: 7400*/
		if (argc == 3) {
			if (!strcmp("-p", argv[1])) {
				sscanf(argv[2], "%i", &port);
			} else {
				printf("Niewlasciwy parametr.\nOpcje: [-p PORT] HOSTNAME\n");
				exit(0);
			}
		} else
			port = MYPORT;

		/* Czytanie pliku konfiguracyjnego */
		/*	vector<vector<string> > data;
		 ifstream infile("conf");

		 while (infile) {
		 string s;
		 if (!getline(infile, s))
		 break;

		 istringstream ss(s);
		 vector<string> record;

		 while (ss) {
		 string s;
		 if (!getline(ss, s, ','))
		 break;
		 record.push_back(s);
		 }

		 data.push_back(record);
		 }
		 if (!infile.eof()) {
		 cerr << "Conf blad!\n";
		 }
		 for (vector<vector<string>>::iterator n = data.begin(); n != data.end(); ++n) {
		 printf("%s",*n);
		 } */

		FILE *fp;
		if ((fp = fopen("conf", "rb")) == NULL) {
			printf("unable to open file ");
			exit(2);
		}
		const int READSIZE = 1024;
		const int BUFFSIZE = READSIZE + 1;
		char buffer[BUFFSIZE]; // + 1 to have place for appending
							   //a terminal character
		vector<string> tokenized_conf;
		int howmuchIread = 0;
		while ((howmuchIread = fread(buffer, 1, READSIZE, fp)) != 0) {
			buffer[howmuchIread] = 0; //place terminal character to avoid overruns
			string sBuffer = buffer;
			if (!feof(fp)) {
				fgets(buffer, READSIZE, fp);
				sBuffer += buffer;
			}
			tokenizestring(sBuffer, ",", tokenized_conf);
		}
		fclose( fp );
/*
		for(std::vector<string>::size_type i=0; i != tokenized_conf.size(); i++) {
				cout << tokenized_conf[i];
		}
*/
		printf("\n*** Start serwera (wpisz \"quit\" aby wyjsc): \n");
		/*Utworzenie socketu dla serwera*/
		server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
		server_address.sin_family = AF_INET;
		server_address.sin_addr.s_addr = htonl(INADDR_ANY);
		server_address.sin_port = htons(port);
		bind(server_sockfd, (struct sockaddr *) &server_address, addresslen);
		/*Nasłuchiwanie klientów*/
		listen(server_sockfd, 1);
		FD_ZERO(&readfds);
		FD_SET(server_sockfd, &readfds);
		FD_SET(0, &readfds);

		while (1) {
			testfds = readfds;
			select(FD_SETSIZE, &testfds, NULL, NULL, NULL);
			/*Sprawdzanie aktywności*/
			for (fd = 0; fd < FD_SETSIZE; fd++) {
				if (FD_ISSET(fd, &testfds)) {

					if (fd == server_sockfd) 
					{
						client_sockfd = accept(server_sockfd,
								(struct sockaddr*) &client_address, &clilen);
						/*Dodawanie klienta do puli*/
						if (num_clients < MAX_CLIENTS) 
						{
							/*Sprawdzanie czy nie ma klienta na czarnej liście*/
							for(int j=0; j<tokenized_conf.size();j++)
							{
								if(inet_ntoa(client_address.sin_addr) == tokenized_conf[i])
								{
									black = 1;
								}
								else
								{
									black = 0;
								}
							}
							/*Dodanie do puli*/
							if(black == 0)
							{
								FD_SET(client_sockfd, &readfds);
								fd_array[num_clients] = client_sockfd;
								printf("Klient %d polaczony\n", ++num_clients);
								sprintf(msg, "c");
								send(client_sockfd, msg, strlen(msg), 0);
							}
							/*Black list*/
							else
							{
								sprintf(msg, "Blokowany adres ip!!!.\n");
								write(client_sockfd, msg, strlen(msg));
								close(client_sockfd);
							}
						}
						/*Przekroczenie liczby klientów podłączonych*/
						else {
							sprintf(msg, "Za duzo klientow.\n");
							write(client_sockfd, msg, strlen(msg));
							close(client_sockfd);
						}
					} else if (fd == 0) // Keyboard activity
							{
						fgets(kb_msg, MSG_SIZE + 1, stdin);
						/*Kończenie pracy serwera - wysyłanie wiadomości do klientów*/
						if (strcmp(kb_msg, "quit\n") == 0) {
							sprintf(msg, "Koniec serwera...\n");
							for (i = 0; i < num_clients; i++) {
								write(fd_array[i], msg, strlen(msg));
								close(fd_array[i]);
							}
							close(server_sockfd);
							exit(0);
						}
						/*Wysyłanie wiadomości z serwera do klientów*/
						else {
							sprintf(msg, "c");
							for (i = 0; i < num_clients; i++) {
								write(fd_array[i], msg, strlen(msg));
							}
						}
					} else if (fd) {

						result = read(fd, msg, MSG_SIZE); // Czytanie danych z otwartego socketu

						if (result == -1)
							perror("read()");
						else if (result > 0) {
							sprintf(kb_msg, "c");
							msg[result] = '\0';

							strcat(kb_msg, msg + 1);
							/*Przesyłanie wiadomości do innych klientów*/
							for (i = 0; i < num_clients; i++) {
								if (fd_array[i] != fd)
									write(fd_array[i], kb_msg, strlen(kb_msg));
							}
							/* Wypisywanie tekstu w serwerze*/
							printf("Klient %d: %s", num_clients, kb_msg + 1);

							if (msg[0] == 'X') //Zamykanie klienta
									{
								exitClient(fd, &readfds, fd_array,
										&num_clients);
							}
						}
					} else {
						exitClient(fd, &readfds, fd_array, &num_clients);
					}
				}
			}
		}
	}

}
